import argparse
import json
import logging
import os
import pathlib
import sys
from logging.handlers import TimedRotatingFileHandler
import win32com.client as win32
import pythoncom

def init_log(name='solidworks', config: dict = None):
    if config is None:
        config = {}

    _logger = logging.getLogger(name)
    if len(_logger.handlers) > 0:
        return _logger

    log_file = config.get('filePath')
    level = config.get('level', 'INFO')
    backup_count = config.get('maxBackups', '7')

    _logger.setLevel(level)
    format_str = logging.Formatter('%(asctime)-15s %(name)s %(process)d %(levelname)-8s %(message)s')

    stream_handler = logging.StreamHandler(stream=sys.stdout)
    stream_handler.setFormatter(format_str)
    _logger.addHandler(stream_handler)

    if log_file:
        rotate_handler = TimedRotatingFileHandler(log_file, when='D', backupCount=int(backup_count))
        rotate_handler.setFormatter(format_str)
        _logger.addHandler(rotate_handler)

    return _logger


def load_driver_config(config_path):
    cp = pathlib.Path(config_path)
    if not cp.is_absolute():
        cp = pathlib.Path(os.getcwd(), config_path)

    if not cp.is_file():
        raise Exception('driver.config "{}" not exists'.format(config_path))

    with open(cp, 'r') as f:
        config = json.load(f)
        if config.get('product') != 'solidworks':
            raise Exception('invalid driver.config')
        return config


def parse_model_parameters(args):
    sw_app = win32.Dispatch('Sldworks.application')
    sw_app.Visible = True
    arg_Nothing = win32.VARIANT(pythoncom.VT_DISPATCH, None)
    model_file = os.path.join(args.model_dir, args.entry)
    print(f'model_file = {model_file}')
    sw_model = sw_app.OpenDoc(f'{model_file}', 1)
    sw_eqn_msg = sw_model.GetEquationMgr
    n_count = sw_eqn_msg.GetCount
    print(f'param count = {n_count}')
    inputs = []
    for i in range(n_count):
        if sw_eqn_msg.GlobalVariable(i):
            equation = sw_eqn_msg.Equation(i)
            name = equation[equation.find("\"")+1:equation.find("\"=")]
            value = sw_eqn_msg.Value(i)
            inputs.append({
                "name": name,
                "equation": equation,
                "value": value,
                "orgValue": value,
                "index": sw_eqn_msg.Status,
                "global": sw_eqn_msg.GlobalVariable(i),
                "mdoLabel": name
            })
    sw_app.CloseDoc(f'{model_file}')
    print(inputs)
    result = {
        "success": True,
        "data": {
            "input": inputs,
            "output": []
        }
    }
    with open(args.output_file_path, 'w') as f:
        json.dump(result, f)


def run_simulation(args):
    sw_app = win32.Dispatch('Sldworks.application')
    sw_app.Visible = True
    arg_Nothing = win32.VARIANT(pythoncom.VT_DISPATCH, None)
    with open(args.sim_config, 'r', encoding="utf-8") as file:
        sim_config = json.load(file)
    model_file = os.path.join(sim_config['model']['file'], sim_config['model']['entry'])
    sw_model = sw_app.OpenDoc(f'{model_file}', 1)
    sw_eqn_msg = sw_model.GetEquationMgr
    parameters = sim_config.get('parameters', [])
    for param in parameters:
        equation = param['equation'].replace(f'{param["orgValue"]}', f'{param["value"]}')
        sw_eqn_msg.Equation(param['index'], equation)
    save_file_name = f"{os.path.splitext(sim_config['model']['entry'])[0]}.{sim_config['extendedProperty']['geoPath'].upper()}"
    save_file_path = os.path.join(os.getcwd(), save_file_name)
    print(f'save file = {save_file_path}')
    sw_model.SaveAs3(save_file_path, 0, 2)
    sw_app.CloseDoc(f'{model_file}')
    result = {
        "success": True,
        "data": {
            "simulationResultDir": "",
            "specifiedParameters": [
                {
                    "name": "geoPath",
                    "mdoLabel": "geoPath",
                    "value": save_file_path,
                    "type": "file"
                }
            ]
        }
    }
    with open(args.output_file_path, 'w') as f:
        json.dump(result, f)


def main(args):
    global logger
    config = load_driver_config(args.driver_config)
    driver_cfg = config.get('config', {})
    logger = init_log(config=driver_cfg.get('log', {}))
    logger.info(args)

    # 运行模型 or 解析模型参数
    if args.subcommand == "run":
        return run_simulation(args)
    elif args.subcommand == "parse":
        return parse_model_parameters(args)
    else:
        raise Exception('invalid sub command')


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='driver for abaqus')

    subparsers = parser.add_subparsers(title='Available Commands', dest="subcommand", help='sub-command is required')
    parser_run = subparsers.add_parser('run', help='run simulation')
    parser_run.add_argument('--driver-config', '-d', type=str, default='driver.config.json', help='driver config')
    parser_run.add_argument('--output-file-path', '-o', type=str, required=True, help='output file path')
    parser_run.add_argument('--sim-config', '-c', type=str, required=True, help='config for simulation task')

    parser_parse = subparsers.add_parser('parse', help='parse configurable parameters')
    parser_parse.add_argument('--output-file-path', '-o', type=str, required=True, help='output file path')
    parser_parse.add_argument('--driver-config', '-d', type=str, default='driver.config.json', help='driver config')
    parser_parse.add_argument('--model-dir', '-m', type=str, required=True, help='model dir')
    parser_parse.add_argument('--entry', '-e', type=str, required=True, help='model file')

    args_ = parser.parse_args()
    if not args_.subcommand:
        parser.print_help()
        sys.exit(1)
    sys.exit(main(args_))